#pragma once
#include"global_var.cpp"
#include"read_atpg.cpp"

void read_netlist(std::string& netlist_path)
{
    std::unordered_set<std::size_t> unique_hash{std::size_t(0)};

    std::fstream netlist(netlist_path.c_str(),std::ios::in);
    std::string cmd;

    while(getline(netlist,cmd))
        if(cmd.find("module")!=std::string::npos)
        {
            while(cmd.find(";")==std::string::npos)
                getline(netlist,cmd);
            break;
        }
    while(getline(netlist,cmd))
    {   
        if(cmd == "") continue;
        else if(cmd.find("endmodule")!=std::string::npos) break;
        else if(cmd.find("output")!=std::string::npos)
        {
            std::string s = cmd;
            while(s.find(";")==std::string::npos)
            {
                getline(netlist,cmd);
                s = s + cmd;
            }
            if(s.find("[")!=std::string::npos)
            {
                int pos_1 = s.find("["),pos_2 = s.find(":"),pos_3 = s.find("]");
                int low,high;
                low= std::stoi(s.substr(pos_1+1,pos_2-pos_1-1));
                high= std::stoi(s.substr(pos_2+1,pos_3-pos_2-1));
                if(high<low) std::swap(high,low);

                ++pos_3;
                for(int pos_4=pos_3+1,sz=s.size();pos_4<sz;)
                {
                    while(s[pos_4]!=','&&s[pos_4]!=';') ++pos_4;
                    std::string tmp_name = s.substr(pos_3,pos_4-pos_3);
                    trim(tmp_name);
                    for(int i=high;i>=low;--i)
                        sys_out.emplace_back(tmp_name+"["+std::to_string(i)+"]");
                    pos_3 = pos_4+1;
                    pos_4 = pos_3+1;
                    
                }
            }
            else
            {
                int l = s.find("output ")+7;
                for(int r=l+1,sz=s.size();r<sz;)
                {
                    while(s[r]!=','&&s[r]!=';') ++r;
                    std::string tmp_name = s.substr(l,r-l);
                    trim(tmp_name);
                    sys_out.emplace_back(tmp_name);
                    l = r+1;
                    r = l+1;
                    
                }
            }
        }
        else if(cmd.find("input")!=std::string::npos)
        {
            std::string s = cmd;
            while(s.find(";")==std::string::npos)
            {
                getline(netlist,cmd);
                s = s + cmd;
            }
            if(s.find("[")!=std::string::npos)
            {
                int pos_1 = s.find("["),pos_2 = s.find(":"),pos_3 = s.find("]");
                int low,high;
                low= std::stoi(s.substr(pos_1+1,pos_2-pos_1-1));
                high= std::stoi(s.substr(pos_2+1,pos_3-pos_2-1));
                if(high<low) std::swap(high,low);

                ++pos_3;
                for(int pos_4=pos_3+1,sz=s.size();pos_4<sz;)
                {
                    while(s[pos_4]!=','&&s[pos_4]!=';') ++pos_4;
                    std::string tmp_name = s.substr(pos_3,pos_4-pos_3);
                    trim(tmp_name);
                    for(int i=high;i>=low;--i)
                        sys_in.emplace_back(tmp_name+"["+std::to_string(i)+"]");
                    pos_3 = pos_4+1;
                    pos_4 = pos_3+1;
                    
                }
            }
            else
            {
                int l = s.find("input ")+6;
                for(int r=l+1,sz=s.size();r<sz;)
                {
                    while(s[r]!=','&&s[r]!=';') ++r;
                    std::string tmp_name = s.substr(l,r-l);
                    trim(tmp_name);
                    sys_in.emplace_back(tmp_name);
                    l = r+1;
                    r = l+1;
                    
                }
            }
        }
        else if(cmd.find("wire")!=std::string::npos)
        {
            while(cmd.find(";")==std::string::npos) 
                getline(netlist,cmd);
        }
        else
        {
            trim(cmd);
            if(cmd=="") continue;
            
            std::string s = cmd;
            while(s.find(";")==std::string::npos) 
            {
                getline(netlist,cmd);
                s = s + cmd;
            }

            int l=0;
            while(s[l]==' ') ++l;
            int r=l+1;
            while(s[r]!=' ') ++r;

            if(!tech_lib.count(s.substr(l,r-l)))
            {
                std::cout<<s.substr(l,r-l)<<" : lib is missing"<<std::endl;
                std::terminate();
            }
            cell* ptr = new cell(tech_lib[s.substr(l,r-l)]);

            while(s[r]!='(') ++r;
            --r;
            while(s[r]==' ') --r;
            l=r-1;
            while(s[l]!=' ') --l;
            std::string tmp_gate_name = s.substr(l,r-l+1);
            trim(tmp_gate_name);

            ptr->name = tmp_gate_name;

            for(auto& [port,in,_1,_2] : ptr->input_port)
            {
                std::string s_port = hash_port[port];
                int l = s.find("."+s_port+"(") + s_port.size()+2;
                int r = l+1;
                while(s[r]!=')') ++r;
                std::string tmp_name = s.substr(l,r-l);
                trim(tmp_name);
                if(!tmp_name.empty())
                {
                    in = generate_string_hash(unique_hash,wire_hash,tmp_name);
                }
            }

            for(auto& [cur_port,name,index,_] : ptr->output_port)
            {
                auto& port = std::get<0>(ptr->wire[index]);
                
                std::string s_port = hash_port[port];

                int l = s.find("."+s_port+"(") +s_port.size()+2;
                int r = l;
                while(s[r]!=')') ++r;
                std::string tmp_name = s.substr(l,r-l);
                trim(tmp_name);
                if(tmp_name != "")
                {
                    name = generate_string_hash(unique_hash,wire_hash,tmp_name);
                    global_ckt[generate_string_hash(unique_hash,wire_hash,tmp_name)] = ptr;
                }
            }

        }
    }
    netlist.close();
    
    for(auto& s : sys_in)
        hash_input.emplace(generate_string_hash(unique_hash,wire_hash,s));
    for(auto& s : sys_out)
        hash_output.emplace(generate_string_hash(unique_hash,wire_hash,s));

    for(auto& [_,ptr] : global_ckt)
        global_name_cell[ptr->name] = ptr;

    for(auto& [_,ptr] : global_name_cell)
    {
        for(auto& [port,in,_,cell_ptr] : ptr->input_port)
        {
            if(hash_input.contains(in)) continue;
            
            cell_ptr = global_ckt[in];

            for(auto& [port_other,output_other,_,vec_cell_ptr] : cell_ptr->output_port)
            {
                if(output_other == in)
                {
                    vec_cell_ptr.emplace_back(ptr);
                    break;
                }
            }
        }
    }

    for(auto& [_,ptr] : global_name_cell)
    {
        for(auto& [_1,_2,_3,v] : ptr->output_port)
        {
            std::unordered_set<cell*> tmp(v.begin(),v.end());
            v = std::vector<cell*>(tmp.begin(),tmp.end());
        }
    }
}